﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Young3.FMSearch.Core.Entities;
using Young3.FMSearch.Core.Entities.InGame;
using Young3.FMSearch.Core.Managers;
using System.Diagnostics;

namespace Young3.FMSearch.Interface
{
    /// <summary>
    /// Interface class to interfer with the ingame world
    /// </summary>
    public class FMContext : IDisposable
    {
        private ObjectManager objectManager = null;
        private GameManager gameManager = null;

        public DatabaseModeEnum DatabaseMode { get; private set; }

        /// <summary>
        /// Fires every time the context has (re)loaded
        /// </summary>
        public event Action GameLoaded = () => { };

        /// <summary>
        /// Create a new FM Context object
        /// </summary>
        /// <param name="databaseMode">Whether the database will be loaded in Realtime or Cached mode</param>
        public FMContext(DatabaseModeEnum databaseMode)
        {
            DatabaseMode = databaseMode;
        }

        /// <summary>
        /// Create a new FM Context object in Realtime mode
        /// </summary>
        public FMContext() 
        {
            //default value is realtime
           DatabaseMode = DatabaseModeEnum.Realtime;
        }

        #region Objects

        /* Lists containing the ingame objects */
        public IEnumerable<Country> Countries { get { return GetListFromStore<Country>(); } }
        public IEnumerable<Player> Players { get { return GetListFromStore<Player>(); } }
        public IEnumerable<Club> Clubs { get { return GetListFromStore<Club>(); } }
        public IEnumerable<Team> Teams { get { return GetListFromStore<Team>(); } }
        public IEnumerable<Continent> Continents { get { return GetListFromStore<Continent>(); } }
        public IEnumerable<City> Cities { get { return GetListFromStore<City>(); } }
        public IEnumerable<Stadium> Stadiums { get { return GetListFromStore<Stadium>(); } }
        public IEnumerable<Staff> Staff { get { return GetListFromStore<Staff>(); } }
        public IEnumerable<HumanManager> HumanManagers { get { return GetListFromStore<HumanManager>(); } }
        public IEnumerable<League> Leagues { get { return GetListFromStore<League>(); } }
        //public IEnumerable<Weather> Weather { get { return GetListFromStore<Weather>(); } }

        /// <summary>
        /// Gets a list of a certain type from the objectstore which only has anonymous objects
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        private ParallelQuery<T> GetListFromStore<T>()
        {
            return ((Dictionary<int, T>)objectManager.ObjectStore[typeof(T)]).Values.AsParallel();
        }

        #endregion

        public bool CheckProcessAndGame()
        {
            if (gameManager == null)
            {
                gameManager = new GameManager();
                gameManager.CheckProcessAndGame();
            }

            return gameManager.FMLoaded();
        }

        public void LoadDataForCheckedGame(bool refreshPersonCache)
        {
            if (objectManager == null)
                objectManager = new ObjectManager(gameManager,DatabaseMode);

            objectManager.Load(refreshPersonCache);

            GameLoaded();
        }

        /// <summary>
        /// Loads the ingame data. Can also be used to refresh your database.
        /// </summary>
        public void LoadData()
        {
            LoadData(false);
        }

        /// <summary>
        /// Loads the ingame data. Can also be used to refresh your database.
        /// </summary>
        /// <param name="refreshPersonCache">Whether the person cache should be flushed. 
        /// Do this every ingame season for regens to show up.</param>
        public void LoadData(bool refreshPersonCache)
        {
            CheckProcessAndGame();
            LoadDataForCheckedGame(refreshPersonCache);
        }

        /// <summary>
        /// Returns ingame meta-data that is context independent
        /// </summary>
        public MetaDataCls MetaData { get { return new MetaDataCls(this, this.objectManager, gameManager); } }

        #region IDisposable Members

        public void Dispose()
        {
        }

        #endregion

        public class MetaDataCls
        {
            private readonly ObjectManager objectManager;
            private readonly FMContext fmContext;
            private readonly Global global;

            internal MetaDataCls(FMContext fmContext, ObjectManager objectManager, GameManager gameManager)
            {
                this.fmContext = fmContext;
                this.objectManager = objectManager;
                global = new Global(gameManager.Version);
            }


            /// <summary>
            /// Returns the current ingame date
            /// </summary>
            public DateTime IngameDate { get { return global.IngameDate; } }

            /// <summary>
            /// Returns the object that is currently displayed in FM.
            /// 
            /// Return value can be
            ///   - Player
            ///   - Staff
            ///   - Team
            ///   - Country
            ///   - League
            ///   
            /// Check to see what type the object is:
            ///     ActiveObject Is Player  (returns bool)
            /// </summary>
            public BaseObject ActiveObject
            {
                get
                {
                    Stopwatch activeObjectSw = Stopwatch.StartNew();

                    int address = global.ActiveObjectAddress;
                    BaseObject returnObject = null;

                    var players = ((Dictionary<int, Player>)objectManager.ObjectStore[typeof(Player)]);
                    var staffs = ((Dictionary<int, Staff>)objectManager.ObjectStore[typeof(Staff)]);
                    var teams = ((Dictionary<int, Team>)objectManager.ObjectStore[typeof(Team)]);
                    var countries = ((Dictionary<int, Country>)objectManager.ObjectStore[typeof(Country)]);
                    var leagues = ((Dictionary<int, League>)objectManager.ObjectStore[typeof(League)]);

                    if (players.ContainsKey(address - 0x114)) returnObject = players[address - 0x114];
                    if (staffs.ContainsKey(address - 68)) returnObject = staffs[address - 68];
                    if (teams.ContainsKey(address)) returnObject = teams[address];
                    if (countries.ContainsKey(address)) returnObject = countries[address];
                    if (leagues.ContainsKey(address)) returnObject = leagues[address];

                    activeObjectSw.Stop();
                    //Debug.WriteLine(string.Format("ActiveObject lookup {0} ms", activeObjectSw.ElapsedMilliseconds));

                    return returnObject;
                }
            }

            /// <summary>
            /// Indicates whether the game is loaded
            /// </summary>
            public bool IsLoaded
            {
                get { return fmContext.gameManager.FMLoaded(); }
            }

            /// <summary>
            /// Returns the Game Version (eg. Steam / DVD) and the version number
            /// </summary>
            public string CurrentVersion
            {
                get { return fmContext.gameManager.Version.Description; }
            }
        }
    }
}

